﻿using System;

namespace accesoDatos
{
    static class ConsultaSQL
    {
        /** Método que prepara consultas simples en lenguaje SQL de manera dinámica.
          * Recibe como parámetros: el nombre de la tabla en la BD que se desea consultar,
          * un arreglo de cadenas que representan las columnas de la tabla anterior que se desean consultar,
          * un arreglo de cadenas que representan los valores para cada una de las columnas del parámetro anterior.
          * REQUERIMIENTOS:
          * - El arreglo de nombres de columnas y valores deben tener la misma longitud.
          * - Cuando un valor es vacío (""), se obvia ese filtro.
          * - Cada valor va precedido de una letra:
          *   * I: búsqueda inexacta por el valor indicado (LIKE).
          *   * E: búsqueda exacta por el valor recibido.
          */
        public static String consultaSimple(String camposSeleccion, String nombreTabla, String[] columnasFiltros, String[] valoresFiltros, string[] orden, int registroInicial, int registroFinal)
        {
            string[] tablasJoin = new string[] { };
            return consultaCompleja(camposSeleccion, nombreTabla, tablasJoin, columnasFiltros, valoresFiltros, orden, registroInicial, registroFinal);
        }

        public static String consultaCompleja(String camposSeleccion, String nombreTabla, String[] tablasJoin, String[] columnasFiltros, String[] valoresFiltros, string[] orden, int registroInicial, int registroFinal)
        {
            String sentenciaSQL = string.Format("SELECT {0} FROM {1}", camposSeleccion, nombreTabla);

            String cadenaJoins = "";
            foreach (string join in tablasJoin) cadenaJoins += string.Format(" {0}", join);
            sentenciaSQL += cadenaJoins;

            String condiciones = getCadenaFiltros(columnasFiltros, valoresFiltros);
            sentenciaSQL += condiciones;

            String cadenaOrden = "";
            foreach (string ord in orden) cadenaOrden += (cadenaOrden.Trim() == "" ? " ORDER BY " : ", ") + ord;
            sentenciaSQL += cadenaOrden;

            // Si los parámetros registroInicial y registroFinal tuvieran un valor distinto al por defecto (-1), se los agrega a la sentencia SQL
            if (registroInicial > -1)
            {
                sentenciaSQL += " LIMIT " + registroInicial;
                if (registroFinal > -1) { sentenciaSQL += ", " + registroFinal; }
            }

            return sentenciaSQL;
        }

        private static String getCadenaFiltros(String[] columnasFiltros, String[] valoresFiltros)
        {
            String condiciones = "";

            // Continúo si hubiera filtros que aplicarle a la consulta, y si la cantidad de filtros y los valores para éstos son iguales
            if (columnasFiltros.Length > 0 && columnasFiltros.Length == valoresFiltros.Length)
            {
                String nombreCol, valor, tipoBusqueda;
                for (int indice = 0; indice < columnasFiltros.Length; indice++)
                {
                    // Columna de la tabla a filtrarse
                    nombreCol = columnasFiltros[indice].Trim();
                    // El primer caracter del valor del filtro, se corresponde con el tipo de búsqueda deseada
                    tipoBusqueda = valoresFiltros[indice].Trim().Length > 0 ? valoresFiltros[indice].Trim().ToUpper().Substring(0, 1) : "E";
                    // El valor por el que se filtrará la columna actual comienza desde la posición 1, ya que la anterior define el tipo de búsqueda
                    valor = valoresFiltros[indice].Trim().Length > 1 ? valoresFiltros[indice].Trim().Substring(1) : "";
                    valor = valor.Replace("'", "\\'");

                    // Sólo si se hubiera enviado un valor para filtrar, se continúa. Sino se saltea el filtro.
                    if (!valor.Equals(""))
                    {
                        condiciones += condiciones.Trim().Equals("") ? " WHERE " : " AND ";
                        String operador;
                        switch (tipoBusqueda)
                        {
                            case "E": operador = "=";
                                break;

                            case "I": operador = "LIKE";
                                break;

                            case "D": operador = ">=";
                                break;

                            case "H": operador = "<=";
                                break;

                            default: operador = "=";
                                break;

                        }
                        if (!operador.Equals("LIKE")) { condiciones += String.Format("{0} {1} '{2}'", nombreCol, operador, valor); }
                        else { condiciones += nombreCol + " LIKE '%" + valor + "%'"; }
                    }
                }
            }

            return condiciones;
        }

        /** Método que prepara consultas en lenguaje SQL para agregar un nuevo elemento a una tabla. Sentencias INSERT
         * Recibe como parámetros: 
         * ** el nombre de la tabla en la BD en la que se desea insertar un elemento,
         * ** un arreglo de cadenas que representan las columnas de la tabla en la que se insertarán datos,
         * ** un arreglo de cadenas que representan los valores para cada una de las columnas del parámetro anterior. El primero debe ser 
         * SIEMPRE el correspondiente al identificador (aún si se genera automáticamente)
         * un booleano que indica si el campo identificador de la tabla se genera de manera automática.
         * REQUERIMIENTOS:
         * - El arreglo de nombres de columnas y valores deben tener la misma longitud.
         * - El primer valor debe ser el correspondiente al campo identificador de la tabla.
         */
        public static String agregarElemento(String nombreTabla, String[] nombresColumnas, String[] valoresCampos, Boolean idAutoGenerado)
        {
            String sentenciaSQL = "", columnas = "", datos = "";

            // Siempre se debe enviar como primer elemento del arreglo de columnas, el identificador de la tabla. Si éste se generase automáticamente
            // en la base de datos, entonces no se lo tiene en cuenta para la inserción en la tabla y se recorre el vector a partir de la posición 1.
            int indiceComienzo = 0;
            if (idAutoGenerado) { indiceComienzo = 1; }

            for (int indice = indiceComienzo; indice < nombresColumnas.Length; indice++)
            {
                columnas += (columnas.Trim().Equals("") ? "" : ", ") + nombresColumnas[indice].Trim();
                datos += (datos.Trim().Equals("") ? "" : ", ") + "'" + valoresCampos[indice].Trim().Replace("'", "\\'") + "'";
            }

            if (!columnas.Equals("") && !datos.Equals("")) { sentenciaSQL = String.Format("INSERT INTO {0} ({1}) VALUES ({2})", nombreTabla, columnas, datos); }
            return sentenciaSQL;
        }

        /** Método que prepara consultas en lenguaje SQL para actualizar registros de una tabla. Sentencia UPDATE.
        * Recibe como parámetros: 
        * ** el nombre de la tabla en la BD en la que se desea actualizar elementos,
        * ** un arreglo de cadenas que representan las columnas de la tabla en la que se modificarán datos,
        * ** un arreglo de cadenas que representan los valores para cada una de las columnas del parámetro anterior.
        * ** un arreglo de cadenas que representan las columnas de la tabla que se utilizarán como condiciones para filtrar los elementos a actualizarse.
        * ** un arreglo de cadenas que representan los valores para cada una de las columnas del parámetro anterior de filtros.
     
        * REQUERIMIENTOS:
        * - El arreglo de nombres de columnas y valores deben tener la misma longitud, tanto para campos a actualizar como filtros.
        */
        public static String actualizarElemento(String nombreTabla, String[] nombresColumnas, String[] valoresCampos, String[] columnasFiltros, String[] valoresFiltros)
        {
            String sentenciaSQL = "", columnas = "", condiciones = "";

            // Valido que las columnas y valores coincidan en tamaños antes de continuar
            if (nombresColumnas.Length == valoresCampos.Length && columnasFiltros.Length == valoresFiltros.Length)
            {
                // Genero la cadena que contiene los nombres de columnas a actualizarse y con qué valores
                for (int indice = 1; indice < nombresColumnas.Length; indice++)
                {
                    columnas += (columnas.Trim().Equals("") ? "" : ", ") + nombresColumnas[indice].Trim() + " = '" + valoresCampos[indice].Trim().Replace("'", "\\'") + "'";
                }

                // Genero la cadena que contiene los nombres de columnas a utilizarse como filtros y con qué valores
                for (int indice = 0; indice < columnasFiltros.Length; indice++)
                {
                    condiciones += (condiciones.Trim().Equals("") ? "" : " and ") + columnasFiltros[indice].Trim() + " = '" + valoresFiltros[indice].Trim().Replace("'", "\\'") + "'";
                }

                if (!columnas.Equals("")) { sentenciaSQL = String.Format("UPDATE {0} SET {1}", nombreTabla, columnas); }
                if (!condiciones.Equals("")) { sentenciaSQL += " WHERE " + condiciones; }
            }

            return sentenciaSQL;
        }

        /** Método que prepara consultas en lenguaje SQL para eliminar registros de una tabla. Sentencia DELETE.
         * Recibe como parámetros: 
         * ** el nombre de la tabla en la BD en la que se desea eliminar elementos,
         * ** un arreglo de cadenas que representan las columnas de la tabla que se utilizarán como condiciones para filtrar los elementos a eliminarse.
         * ** un arreglo de cadenas que representan los valores para cada una de las columnas del parámetro anterior de filtros.
     
         * REQUERIMIENTOS:
         * - El arreglo de nombres de columnas y valores deben tener la misma longitud.
         */
        public static String eliminarElemento(String nombreTabla, String[] columnasFiltros, String[] valoresFiltros)
        {
            String sentenciaSQL = "DELETE FROM " + nombreTabla;
            String condiciones = "";

            if (columnasFiltros.Length == valoresFiltros.Length)
            {
                // Genero la cadena que contiene los nombres de columnas a utilizarse como filtros y con qué valores
                for (int indice = 0; indice < columnasFiltros.Length; indice++)
                {
                    condiciones += (condiciones.Trim().Equals("") ? "" : " and ") + columnasFiltros[indice].Trim() + " = '" + valoresFiltros[indice].Trim().Replace("'", "\\'") + "'";
                }

                if (!condiciones.Equals("")) { sentenciaSQL += " WHERE " + condiciones; }
            }

            return sentenciaSQL;
        }

        /** Método que prepara consultas en lenguaje SQL para encontrar elementos con ciertos valores iguales y distinto número identificador.
         * Recibe como parámetros: el nombre de la tabla en la BD que se desea consultar,
         * ** el nombre de la tabla en la BD en la que se desea buscar elementos,
         * ** el nombre del campo de la tabla que hace de identificador único del elemento (id)
         * ** el valor para el identificador correspondiente al campo anterior
         * ** un arreglo de cadenas que representan las columnas de la tabla que se utilizarán como condiciones para buscar elementos duplicados.
         * ** un arreglo de cadenas que representan los valores para cada una de las columnas del parámetro anterior.
         * 
         * REQUERIMIENTOS:
         * - El arreglo de nombres de columnas y valores deben tener la misma longitud.
         * - Cuando un valor es vacío (""), se obvia ese filtro.
         */
        public static String registroDuplicado(String nombreTabla, String campoID, String valorID, String[] columnasFiltros, String[] valoresFiltros)
        {
            String sentenciaSQL = String.Format("SELECT * FROM {0} WHERE {1} != '{2}'", nombreTabla, campoID, valorID);
            String condiciones = "", nombreCol, valor;

            if (columnasFiltros.Length > 0 && columnasFiltros.Length == valoresFiltros.Length)
            {
                for (int indice = 0; indice < columnasFiltros.Length; indice++)
                {
                    nombreCol = columnasFiltros[indice].Trim();
                    valor = valoresFiltros[indice].Trim().Replace("'", "\\'");

                    if (!valor.Equals("")) { condiciones += String.Format(" and {0} = '{1}'", nombreCol, valor); }
                }
            }

            sentenciaSQL += condiciones;
            return sentenciaSQL;
        }
    }
}